home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / Control Panel 0.9.4 / MyPatch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-02  |  12.7 KB  |  359 lines  |  [TEXT/KAHL]

  1. /*
  2.     MyPatch.c
  3.  
  4.     Written by Ken Worley, 02/01/94, using Symantec Think C 6.0.1. Copyright 1994.
  5.     
  6.     Feel free to use this code in a patch of your own.  Please don't
  7.     publish or distribute this code without giving me proper credit.
  8.     
  9.     This is the 'task' or 'patch' portion of the code.  The INIT or extension
  10.     portion of the code runs at system startup time, loads this piece of code into
  11.     memory, and installs it as a patch to a trap.
  12.     
  13.     This code shares a data structure with the extension portion of the code,
  14.     the control panel code, and possibly with other code as well.  One of the fields
  15.     in this shared data is called CPon.  This field is initially set by the
  16.     extension based on the values in the 'pref' resource.  The control panel code
  17.     may modify it later based on the user's choices in the control panel.  The
  18.     field determines whether we take action here, or jump to the original trap
  19.     without making any modifications.
  20.     
  21.     A pointer here is assigned to the address 0x12345678.  When the extension code
  22.     loads this code into memory, it replaces that bogus address with the address
  23.     of the shared data structure in memory.
  24.     
  25.     Using routines in the ANSI-A4 library (like vsprintf) requires that we set up
  26.     the A4 register here for access to globals.  Curiously, when I failed to do
  27.     that, the end result worked fine on my Performa 405 and crashed my Classic.
  28.     
  29. */
  30.  
  31. /* PROTOTYPES */
  32.  
  33. void    main( void );
  34. Boolean    StrCompare( Str255 str1, Str255 str2 );
  35.  
  36. /* INCLUDED FILES */
  37.  
  38. #include "Notifier.h"        /* prototypes and includes for StrFormat and StrNotify */
  39.  
  40. #include "SharedData.h"        /* The definition of our shared data structure. */
  41.                             /* Includes definitions of myDataStruct, myDataPtr, */
  42.                             /* and myDataHandle to refer to this data. */
  43.  
  44. #include <SetUpA4.h>
  45.  
  46. /* FUNCTIONS */
  47.  
  48. void    main( void )
  49. {
  50.     myDataPtr        myData;            /* a pointer to our shared data */
  51.     long            oldTrapLoc;        /* local var to hold the old trap routine addr */
  52.     long            pBytes, rBytes;    /* vars for # of bytes in params & return val */
  53.     Boolean            callOriginal;    /* this determines whether we call orig trap */
  54.     Ptr                paramAddr;        /* set to the addr of the params on the stack */
  55.     Ptr                returnAddr;        /* set to the addr of the ret val on the stack */
  56.     Boolean            takeAction;        /* this flag determines whether this trap has */
  57.                                     /* any real effect */
  58.     
  59.     /*    Set up the A4 register for access to statics and globals. */
  60.     
  61.         RememberA0();
  62.         SetUpA4();
  63.         
  64.     /*    Here's where we assign a bogus address to our data structure pointer.
  65.      *    Again, this will be edited by the extension code so that it becomes a
  66.      *    valid address (using the Munger routine).  You won't need to change this.
  67.      */
  68.         
  69.         myData = (myDataPtr)(0x12345678);
  70.     
  71.     /*    Now wait until the shared data structure isn't in use, then flag it as
  72.      *    in use until we're through with it.
  73.      */
  74.      
  75.          while ( myData->inUse ) {}
  76.          myData->inUse = true;
  77.     
  78.     /*    Set the takeAction flag which will determine if our modifications get
  79.      *    executed or the old trap gets called and no modified code is executed.
  80.      */
  81.      
  82.          takeAction = myData->CPon;
  83.          
  84.     /*    Get the old trap location from the shared data structure.  Also get the
  85.      *    size of the parameters and return value for the trap.  You shouldn't need
  86.      *    to change this.
  87.      */
  88.     
  89.         oldTrapLoc = myData->oldTrap;
  90.         pBytes = myData->paramBytes;
  91.         rBytes = myData->returnBytes;
  92.     
  93.     /*    Get the addresses of the parameters sent to the trap and the space reserved
  94.      *    on the stack for the return value.  We can find these as an offset from A6.
  95.      *    The address of the parameters goes into paramAddr and the address of the
  96.      *    space reserved for the return value goes into returnAddr.  You shouldn't
  97.      *    need to change this (even if it's a register based routine in which case the
  98.      *    values would have no meaning).
  99.      */
  100.      
  101.          asm {
  102.              MOVE.L    A6,A0            ;copy A6 into A0
  103.              ADD.L    #8,A0            ;add 8 to get addr of original params
  104.              MOVE.L    A0,paramAddr    ;copy addr of params into local var paramAddr
  105.              ADD.L    pBytes,A0        ;add # of param bytes to get addr of ret value
  106.              MOVE.L    A0,returnAddr    ;copy addr of ret value into local var returnAddr
  107.          }
  108.  
  109.     /*    Any changes to the original parameters should be done here (usually if the
  110.      *    original trap is going to be called).  Also, changes to the return value
  111.      *    can be made here (usually when the original trap is NOT going to be called).
  112.      *    The local var returnAddr is a pointer to the place in the stack where the
  113.      *    return value is.  Remember, before we call the original trap there is no
  114.      *    return value...only a space for one so the value is undefined.  If we're not
  115.      *    going to call the original trap, then we can set the return value here to
  116.      *    whatever we want to be returned to the calling program.  If you modify the
  117.      *    return value here, then call the original trap, the value will be overwritten
  118.      *    with the value returned from the original trap.  The easiest way to access
  119.      *    the return value would be to declare a local variable as a pointer to
  120.      *    whatever the return type is, then assign returnAddr to that variable casting
  121.      *    it to a pointer to the correct type.
  122.      */
  123.     
  124.         if ( takeAction )
  125.         {
  126.         
  127.             /*    I don't do anything with the parameters before the original trap
  128.              *    is called in this case :-).
  129.              *
  130.              *    Example:  If the return value was a Point (same size as a long, 4 bytes)
  131.              *    then we could easily access it as follows:
  132.              *
  133.              *    Declare a local variable at the beginning of main like so:
  134.              *
  135.              *        Point    *myPoint;
  136.              *
  137.              *    Then assign returnAddr to myPoint casting it as a pointer to a Point:
  138.              *
  139.              *        myPoint = (Point*)returnAddr;
  140.              *        
  141.              *    Now we can access the value:
  142.              *
  143.              *        myPoint->h = whatever;
  144.              */
  145.              
  146.          }
  147.     
  148.     /*    Set callOriginal so that the original trap will be called.  You may want to
  149.      *    change this so that callOriginal is set based on certain conditions.  I just
  150.      *    set it to true here so that the original trap is called.  You may want to
  151.      *    just set it to false to avoid calling the original trap if you set the
  152.      *    return value yourself or just don't want the trap to have any effect (when
  153.      *    there is no return value).
  154.      */
  155.     
  156.         callOriginal = true;
  157.     
  158.     /*    If the callOriginal flag is true OR if we're not supposed to take any action
  159.      *    here, call the original trap.
  160.      */
  161.      
  162.     if ( callOriginal || !takeAction )
  163.     {
  164.         /*    Make space for a return value (if any) and copy the original parameters to
  165.          *    the top of the stack.  Then jump to the old trap location using JSR so
  166.          *    that execution will return here.  You shouldn't need to change this.
  167.          */
  168.      
  169.              asm {
  170.                  SUB.L    rBytes,SP        ;make space on stack for return value
  171.                  SUB.L    pBytes,SP        ;make space for parameters on stack
  172.                  
  173.                  MOVE.L    paramAddr,A0    ;copy addr of orig params into A0 (source)
  174.                  MOVE.L    SP,A1            ;put addr of top of stack into A1 (dest)
  175.                  MOVE.L    pBytes,D0        ;put number of bytes to move into D0
  176.                  _BlockMove                ;copy original params to top of stack
  177.              
  178.                  MOVE.L    oldTrapLoc,A0    ;put addr of orig trap into A0
  179.                  JSR        (A0)            ;jump to the original trap
  180.              }
  181.  
  182.         /*    Execution returns here after calling the original trap and the function
  183.           *    result is left on top of the stack.  Copy the new result into the space
  184.           *    allocated for the result when we were originally called, then pop the
  185.           *    new result value off the stack.
  186.           *    This code only executes if there is a return value (if rBytes is nonzero).
  187.           *    You shouldn't need to change this.
  188.           */
  189.               
  190.               if ( rBytes )
  191.               {
  192.                   asm {
  193.                       MOVE.L    SP,A0                ;move addr of top of stack to A1 (source)
  194.                       MOVE.L    returnAddr,A1        ;move addr of old ret val to A0 (dest)
  195.                       MOVE.L    rBytes,D0            ;number of bytes in return value
  196.                       _BlockMove                    ;copy new return value to old
  197.  
  198.                     ADD.L    rBytes,SP            ;pop new return value off stack
  199.                   }
  200.               }
  201.      }    /* end if ( callOriginal || !takeAction ) */
  202.      
  203.     /*    If you need to modify the return value, do so here.  Its addr is still in
  204.      *    returnAddr.  If the original trap was called, the value it returned (if any)
  205.      *    was copied to returnAddr.  Also, paramAddr does still point to the parameters
  206.      *    if you need to access them still.  Modifying them at this point, however, would
  207.      *    have no effect since we're just about to pop them off the stack into oblivion.
  208.      *    Remember if it was a pointer or handle that was sent as a parameter, that value
  209.      *    won't have changed, but the data that it points to may have.  If you did NOT
  210.      *    call the original trap and do NOT modify the return value here, and there IS
  211.      *    a return value, you'll be returning GARBAGE to the calling program (bad idea).
  212.      *
  213.      *    I believe that taking action at this point is what's known as a "tail patch"
  214.      *    which is generally considered to be "ill-advised."  The way this is set up,
  215.      *    however, I don't see how anything unforseen could happen as long as we're
  216.      *    careful.
  217.      */
  218.      
  219.      if ( takeAction )
  220.      {
  221.      
  222.          /*    The return value from MenuSelect is a long integer value whose high word
  223.           *    contains the menu id (zero if none selected) and whose low word contains
  224.           *    the item number selected in that menu (undefined if none selected).
  225.           *    Here I check to see if the menu item selected was "About This Macintosh..."
  226.           *    and if it was, I put up a Notification to brag about our hacking prowess.
  227.           *    Then I return the return value unchanged and the Macintosh info dialog
  228.           *    will come up.
  229.           */
  230.           
  231.               {    /* isolate our code and variables here in a block for neatness' sake */
  232.               
  233.                   short        *menuInfo;
  234.                   MenuHandle    menuH;
  235.                   Str255        itemString;
  236.                   Str255        compString = "\pAbout This Macintosh…";
  237.                   
  238.                   /* By the way, the three dots at the end of some menu items are NOT */
  239.                   /* three periods, they are one character--an option semi-colon */
  240.                   
  241.               /*    Assign returnAddr to menuInfo - a pointer to a short */
  242.               
  243.                   menuInfo = (short*)returnAddr;
  244.               
  245.               /*    Now we can use menuInfo[0] to access the high word (menu id#)
  246.                *    and menuInfo[1] to access the low word (the item#).
  247.                */
  248.                    if ( menuInfo[0] )    /* if a menu item was selected, will be nonzero */
  249.                    {
  250.                        menuH = GetMHandle( menuInfo[0] );    /* get a handle to the menu */
  251.                        GetItem( menuH, menuInfo[1], itemString );    /* get menu item text */
  252.                        
  253.                        if ( StrCompare( itemString, compString ) )
  254.                        {
  255.                            /* Here's an example of using the Notification manager through
  256.                             * the StrFormat and StrNotify routines.  StrFormat works like
  257.                             * printf.  Do NOT put a "\p" at the beginning of the string.
  258.                             */
  259.                         StrFormat( myData->str, "Ha! Ha!  Hackers do it "
  260.                             "at system level!  "
  261.                             "Menu #%d, Item #%d.", menuInfo[0], menuInfo[1] );
  262.                         StrNotify( &(myData->nm), &(myData->str) );
  263.                        }
  264.                    }
  265.               }
  266.               
  267.      }    /* end if ( myData->patchAction )
  268.      
  269.      /*    Now we're through with the shared data */
  270.      
  271.          myData->inUse = false;
  272.      
  273.      /* Restore the A4 register to its previous value */
  274.      
  275.          RestoreA4();
  276.          
  277.      /*    Finish up by unlinking A6 (Think C automatically links A6 on entry
  278.       *    to the routine.  It also unlinks it automatically at the end of the routine,
  279.       *    but we're going to jump out before the actual end of the routine.),
  280.       *    removing the original parameters from the stack, and jumping to the return
  281.       *    address.  You shouldn't need to change this.
  282.       */
  283.           
  284.           asm {
  285.               MOVE.L    pBytes,D0    ;save the number of bytes in the parameters into D0
  286.               UNLK    A6            ;unlinking leaves return addr at the top of the stack
  287.               MOVE.L    (SP)+,A0    ;move return address to register A0
  288.               ADD.L    D0,SP        ;pop old parameters off stack
  289.               JMP        (A0)        ;jump to the return address
  290.           }
  291. }
  292.  
  293. #include "Notifier.c"    /* contains StrFormat and StrNotify */
  294.  
  295. Boolean    StrCompare( Str255 str1, Str255 str2 )
  296. {
  297.     /*    This function returns true if the two strings sent are equal.  The comparison
  298.      *    is case sensitive and trailing blanks are ignored.  If one string is longer
  299.      *    and contains something OTHER than blanks after the end of the other string,
  300.      *    the function will return false.
  301.      */
  302.  
  303.     short    shorter,    /* which string is shorter? */
  304.             endShort,    /* last character of the shorter string */
  305.             endLong;    /* last character of the longer string */
  306.     Boolean    result;
  307.     
  308.     result = true;    /* assume they're equal until we find otherwise */
  309.     
  310.     shorter = 1;
  311.     endShort = str1[0];
  312.     endLong = str2[0];
  313.     
  314.     if ( str2[0] < str1[0] )
  315.     {
  316.         shorter = 2;
  317.         endShort = str2[0];
  318.         endLong = str1[0];
  319.     }
  320.     
  321.     {                /* check each letter to see if they're equal */
  322.         short x;
  323.         for ( x = 1; x <= endShort; x++ )
  324.         {
  325.             if ( str1[x] != str2[x] )
  326.             {
  327.                 result = false;    /* not equal */
  328.                 break;
  329.             }
  330.         }
  331.     }
  332.     
  333.     if ( result )    /* check for non-blank trailing characters */
  334.     {
  335.         short x;
  336.         for ( x = endShort+1; x <= endLong; x++ )
  337.         {
  338.             if ( shorter == 1 )
  339.             {
  340.                 if ( str2[x] != ' ' )
  341.                 {
  342.                     result = false;    /* not a blank */
  343.                     break;
  344.                 }
  345.             }
  346.             else
  347.             {
  348.                 if ( str1[x] != ' ' )
  349.                 {
  350.                     result = false; /* not a blank */
  351.                     break;
  352.                 }
  353.             }
  354.         }
  355.     }
  356.     return result;
  357. }
  358.  
  359.